﻿using UnityEditor;
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Obi{
	
	/**
	 * Custom inspector for ObiStitcher component. 
	 */
	
	[CustomEditor(typeof(ObiStitcher))] 
	public class ObiStitcherEditor : Editor
	{
		
		ObiStitcher stitcher;
		static public bool editing = false;

		static public Vector3 sewingToolHandle1 = Vector3.zero;
		static public Vector3 sewingToolHandle2 = Vector3.one;

		static public bool[] selectionStatus = new bool[0];
		
		public void OnEnable(){
			stitcher = (ObiStitcher)target;

			// initialize sewing tool to sensible values:
			if (stitcher.Actor1 != null && stitcher.Actor2 != null){
				sewingToolHandle1 = stitcher.Actor1.transform.position;
				sewingToolHandle2 = stitcher.Actor2.transform.position;
			}
		}
		
		public override void OnInspectorGUI() {
			
			serializedObject.UpdateIfRequiredOrScript();

			EditorGUI.BeginChangeCheck();
			ObiActor actor1 = EditorGUILayout.ObjectField("First actor",stitcher.Actor1, typeof(ObiActor),true) as ObiActor;
			if (EditorGUI.EndChangeCheck()){
				Undo.RecordObject(stitcher, "Set first actor");
				stitcher.Actor1 = actor1;
                if (actor1 != null)
                    sewingToolHandle1 = actor1.transform.position;
                PrefabUtility.RecordPrefabInstancePropertyModifications(stitcher);
			}

			EditorGUI.BeginChangeCheck();
			ObiActor actor2 = EditorGUILayout.ObjectField("Second actor",stitcher.Actor2, typeof(ObiActor),true) as ObiActor;
			if (EditorGUI.EndChangeCheck()){
				Undo.RecordObject(stitcher, "Set second actor");
				stitcher.Actor2 = actor2;
                if (actor2 != null)
                    sewingToolHandle2 = actor2.transform.position;
                PrefabUtility.RecordPrefabInstancePropertyModifications(stitcher);
            }

			if (stitcher.Actor1 != null && stitcher.Actor2 != null && stitcher.Actor1.solver != stitcher.Actor2.solver){
				EditorGUILayout.HelpBox("Both actors must be managed by the same solver.",MessageType.Error);
			}

			EditorGUILayout.HelpBox("Stitch count: " + stitcher.StitchCount,MessageType.None);

			// edit mode:
			GUI.enabled = stitcher.Actor1 != null && stitcher.Actor2 != null;
			editing = GUILayout.Toggle(editing,"Edit","LargeButton");

			if (editing){

				// Clear all stitches
				if (GUILayout.Button("Clear all stitches")){
					if (EditorUtility.DisplayDialog("Clearing stitches","Are you sure you want to remove all stitches?","Ok","Cancel")){
						Undo.RecordObject(stitcher, "Clear all stitches");
						stitcher.Clear();
                        PrefabUtility.RecordPrefabInstancePropertyModifications(stitcher);
                    }
				}
	
				// Remove selected stitches
				if (GUILayout.Button("Remove selected stitches")){

					List<int> removedStitches = new List<int>();

					for(int i = 0; i < selectionStatus.Length; ++i){
						if (selectionStatus[i]){
							removedStitches.Add(i);
							selectionStatus[i] = false;
						}
					}

					if (removedStitches.Count > 0){
	
						Undo.RecordObject(stitcher, "Remove stitches");

						// Remove from last to first, to avoid throwing off subsequent indices:
						foreach(int i in removedStitches.OrderByDescending(i => i)){
							stitcher.RemoveStitch(i);
						}
                        PrefabUtility.RecordPrefabInstancePropertyModifications(stitcher);
                    }
				}

				// Add stitch:
				if (GUILayout.Button("Add Stitch"))
                {
                    FindClosestParticles(out int particle1, out int particle2);

                    if (particle1 >= 0 && particle2 >= 0)
                    {
                        Undo.RecordObject(stitcher, "Add stitch");
                        stitcher.AddStitch(particle1, particle2);
                        PrefabUtility.RecordPrefabInstancePropertyModifications(stitcher);
                    }
                }
			}
			GUI.enabled = true;

			// Apply changes to the serializedProperty
			if (GUI.changed){
				
				serializedObject.ApplyModifiedProperties();
				
				//stitcher.PushDataToSolver(ParticleData.NONE);
				
			}
			
		}

		public void FindClosestParticles(out int particle1, out int particle2)
        {
            particle1 = -1;
            particle2 = -1;
            float minDistance = float.MaxValue;

            if (stitcher.Actor1 == null || stitcher.Actor2 == null)
                return;

            var handle1 = HandleUtility.WorldToGUIPointWithDepth(sewingToolHandle1);
            var handle2 = HandleUtility.WorldToGUIPointWithDepth(sewingToolHandle2);

            if (stitcher.Actor1 == stitcher.Actor2)
            {
				float minDistance2 = float.MaxValue;
                for (int i = 0; i < stitcher.Actor1.activeParticleCount;++i)
                {
					Vector3 pos = stitcher.Actor1.GetParticlePosition(stitcher.Actor1.solverIndices[i]);
                    pos = HandleUtility.WorldToGUIPointWithDepth(pos);

					float distance1 = (pos - handle1).sqrMagnitude;
					float distance2 = (pos - handle2).sqrMagnitude;
					if (distance1 < minDistance){
						minDistance = distance1;
						particle1 = i;
					}
					if (distance2 < minDistance2){
						minDistance2 = distance2;
						particle2 = i;
					}
				}
			}else{

				// find closest particle to each end of the sewing tool:
                for (int i = 0; i < stitcher.Actor1.activeParticleCount; ++i)
                {
					Vector3 pos = stitcher.Actor1.GetParticlePosition(stitcher.Actor1.solverIndices[i]);
                    pos = HandleUtility.WorldToGUIPointWithDepth(pos);

                    float min = (pos - handle1).sqrMagnitude;
					if (min < minDistance)
                    {
						minDistance = min;
						particle1 = i;
					}
				}
		
				minDistance = float.MaxValue;
                for (int i = 0; i < stitcher.Actor2.activeParticleCount; ++i)
                {
					Vector3 pos = stitcher.Actor2.GetParticlePosition(stitcher.Actor2.solverIndices[i]);
                    pos = HandleUtility.WorldToGUIPointWithDepth(pos);

					float min = (pos - handle2).sqrMagnitude;
					if (min < minDistance)
                    {
						minDistance = min;
						particle2 = i;
					}
				}
			}
		}

		public void DrawSewingTool()
        {

            FindClosestParticles(out int particle1, out int particle2);

            if (particle1 >= 0 && particle2 >= 0)
            {
                sewingToolHandle1 = stitcher.Actor1.GetParticlePosition(stitcher.Actor1.solverIndices[particle1]);
                sewingToolHandle2 = stitcher.Actor2.GetParticlePosition(stitcher.Actor2.solverIndices[particle2]);

                float radius1 = stitcher.Actor1.GetParticleMaxRadius(stitcher.Actor1.solverIndices[particle1]);
                float radius2 = stitcher.Actor2.GetParticleMaxRadius(stitcher.Actor2.solverIndices[particle2]);

                Handles.color = Color.white;
#if (UNITY_2022_1_OR_NEWER)
                sewingToolHandle1 = Handles.FreeMoveHandle(sewingToolHandle1, radius1 * 2, new Vector3(.5f,.5f,.5f),Handles.SphereHandleCap);
			    sewingToolHandle2 = Handles.FreeMoveHandle(sewingToolHandle2, radius2 * 2, new Vector3(.5f,.5f,.5f),Handles.SphereHandleCap);
#else
                sewingToolHandle1 = Handles.FreeMoveHandle(sewingToolHandle1, Quaternion.identity, radius1 * 2, new Vector3(.5f, .5f, .5f), Handles.SphereHandleCap);
                sewingToolHandle2 = Handles.FreeMoveHandle(sewingToolHandle2, Quaternion.identity, radius2 * 2, new Vector3(.5f, .5f, .5f), Handles.SphereHandleCap);
#endif

                Vector3 direction = Vector3.Normalize(sewingToolHandle2 - sewingToolHandle1);
                Handles.color = Color.yellow;
                ObiEditorUtils.DrawArrowHandle(sewingToolHandle1 + direction*(radius1 + 0.05f), sewingToolHandle2 - direction*(radius2+0.05f)); 
            }
		}

		/**
		 * Draws selected stitches in the scene view and allows their selection.
		 */
		public void OnSceneGUI(){

			Array.Resize(ref selectionStatus,stitcher.StitchCount);

			if (!editing)
				return;
			
			DrawSewingTool();

			if (stitcher.Actor1 != null && stitcher.Actor2 != null){

				int controlID = GUIUtility.GetControlID("stitcher".GetHashCode(),FocusType.Passive);
				float distanceToClosest = float.MaxValue;
				int selectedIndex = -1;
				int i = 0;

				foreach(ObiStitcher.Stitch stitch in stitcher.Stitches){
					
					Vector3 pos1 = stitcher.Actor1.GetParticlePosition(stitcher.Actor1.solverIndices[stitch.particleIndex1]);
					Vector3 pos2 = stitcher.Actor2.GetParticlePosition(stitcher.Actor2.solverIndices[stitch.particleIndex2]);
	
					switch (Event.current.GetTypeForControl(controlID)){
						case EventType.MouseDown: 
			
							if (Event.current.button != 0) break;

							// If the user is pressing shift, accumulate selection.
							if ((Event.current.modifiers & EventModifiers.Shift) == 0 && (Event.current.modifiers & EventModifiers.Alt) == 0){
								for(int j = 0; j < selectionStatus.Length; j++)
									selectionStatus[j] = false;
							}

							float distance = HandleUtility.DistanceToLine(pos1,pos2);
							if (distance < 10 && distance < distanceToClosest){

								distanceToClosest = distance;
								selectedIndex = i;
				
								// Prevent deselection if we have selected a stitch:
								GUIUtility.hotControl = controlID;
								Event.current.Use();

							}
						break;
						case EventType.Repaint:
							Handles.color = selectionStatus[i]?Color.red:Color.cyan;
							Handles.DrawDottedLine(pos1,pos2,2);
						break;
					}
					++i;
				}

				if (selectedIndex >= 0){
					selectionStatus[selectedIndex] = !selectionStatus[selectedIndex];
				}

			}
			
		}
		
	}
}

